package com.ericsson.smart.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ericsson.smart.UtilityCommon;
import com.ericsson.smart.UtilityDimensionDAO;
import com.ericsson.smart.factory.UtilityDimnDAOFactory;
import com.ericsson.smart.model.Dimension;

@Component
public class FactDAO {
	//UtilityDimnDAOFactory dimensionDAOFactory = new UtilityDimnDAOFactory();

	private List<String> dimensionList;

	private Map<String, Map<String, Object>> dimensionDataMap;

	private Map<String, List<String>> lowestLevelIdListMap;

	private String xDimDisplayLevel;

	private List<String> xAxisDisplayLevelIds;

	Map<String, String> groupedXdimIdMap;

	@Autowired
	UtilityDimensionDAO dimensionDAO;
	
	@Autowired
	UtilityCommon utilCommon;

	public <T> Map<String, Float> fetchReportData(List<Map<String, Object>> dimDataMapList,
			List<String> dimList, String aggregateOperation, boolean nextLevelGroupingRequired,
			Class<T> genericClass) {
		System.out.println("Inside fetchReportData() method...");
		Map<String, Float> groupedFactDataDisplayLevelWise = null;
		boolean showZeroData = false;

		if (aggregateOperation == null) {
			System.out.println("ERROR: AGGREGATE Operation specified is: " + aggregateOperation
					+ " .Supported opertions are SUM, AVG, MAX, MIN.");
			return null;
		} else if (aggregateOperation.trim().equals("")) {
			System.out.println("ERROR: AGGREGATE Operation specified is: " + aggregateOperation
					+ " .Supported opertions are SUM, AVG, MAX, MIN.");
			return null;
		} else {
			aggregateOperation = aggregateOperation.trim().toUpperCase();
		}

		if (!aggregateOperation.equals("SUM") && !aggregateOperation.equals("AVG")
				&& !aggregateOperation.equals("MAX") && !aggregateOperation.equals("MIN")) {
			System.out.println("ERROR: The AGGREGATE Operation: " + aggregateOperation
					+ " :is NOT SUPPORTED. Supported opertions are SUM, AVG, MAX, MIN.");
			return null;
		}

		pouplateDimensions(dimDataMapList, dimList);
		pouplateLowestLevelIds();

		if (dimensionList.get(0).equals("DIM_CUSTOMER")) {
			System.out.println("Inside if after pouplateLowestLevelIds() method....");
			xDimDisplayLevel = "CUSTOMER_ID";
			xAxisDisplayLevelIds = getDisplayLevelIds(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel);
			groupedXdimIdMap = groupXaxisIdsByDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), "CUSTOMER_CATEGORY",
					xAxisDisplayLevelIds);
		} else {
			System.out.println("Inside else after pouplateLowestLevelIds() method....");
			xDimDisplayLevel = getXdimDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), nextLevelGroupingRequired);
			xAxisDisplayLevelIds = getDisplayLevelIds(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel);
			groupedXdimIdMap = groupXaxisIdsByDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel,
					xAxisDisplayLevelIds);
		}
		System.out.println("Call getDataFromFactTable() method...");
		Map<String, String> factTableValueMap = getDataFromFactTable();

//		if(dimensionList.get(0).equals("DIM_SUBSTATION")) {
//			showZeroData = false;			
//		} else {
//			showZeroData = true;
//		}		
		
		groupedFactDataDisplayLevelWise = groupFactTableDataDisplayLabelWise(aggregateOperation,
				factTableValueMap, groupedXdimIdMap, dimensionList.get(0), xDimDisplayLevel,
				xAxisDisplayLevelIds, showZeroData);

		return groupedFactDataDisplayLevelWise;
	}
	
	public <T> Map<String, Float> fetchAlarmData(List<Map<String, Object>> dimDataMapList,
			List<String> dimList, String aggregateOperation, boolean nextLevelGroupingRequired,
			Class<T> genericClass) {

		Map<String, Float> groupedFactDataDisplayLevelWise = null;
		boolean showZeroData = false;

		if (aggregateOperation == null) {
			System.out.println("ERROR: AGGREGATE Operation specified is: " + aggregateOperation
					+ " .Supported opertions are SUM, AVG, MAX, MIN.");
			return null;
		} else if (aggregateOperation.trim().equals("")) {
			System.out.println("ERROR: AGGREGATE Operation specified is: " + aggregateOperation
					+ " .Supported opertions are SUM, AVG, MAX, MIN.");
			return null;
		} else {
			aggregateOperation = aggregateOperation.trim().toUpperCase();
		}

		if (!aggregateOperation.equals("SUM") && !aggregateOperation.equals("AVG")
				&& !aggregateOperation.equals("MAX") && !aggregateOperation.equals("MIN")) {
			System.out.println("ERROR: The AGGREGATE Operation: " + aggregateOperation
					+ " :is NOT SUPPORTED. Supported opertions are SUM, AVG, MAX, MIN.");
			return null;
		}

		pouplateDimensions(dimDataMapList, dimList);
		pouplateLowestLevelIds();

		if (dimensionList.get(0).equals("DIM_CUSTOMER")) {
			xDimDisplayLevel = "CUSTOMER_ID";
			xAxisDisplayLevelIds = getDisplayLevelIds(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel);
			groupedXdimIdMap = groupXaxisIdsByDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), "CUSTOMER_CATEGORY",
					xAxisDisplayLevelIds);
		} 
		else if(dimensionList.get(0).equals("DIM_TRANSFORMER_METER_TYPE")) {
			xDimDisplayLevel = "TYPE_ID";
			xAxisDisplayLevelIds = getDisplayLevelIds(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel);
			groupedXdimIdMap = groupXaxisIdsByDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), "ITEM",
					xAxisDisplayLevelIds);
		}
		else if(dimensionList.get(0).equals("DIM_SEVERITY")) {
			xDimDisplayLevel = "SEVERITY_ID";
			xAxisDisplayLevelIds = getDisplayLevelIds(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel);
			groupedXdimIdMap = groupXaxisIdsByDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), "SEVERITY_NAME",
					xAxisDisplayLevelIds);
			
		}
		else {
			xDimDisplayLevel = getXdimDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), nextLevelGroupingRequired);
			xAxisDisplayLevelIds = getDisplayLevelIds(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel);
			groupedXdimIdMap = groupXaxisIdsByDisplayLevel(dimensionList.get(0),
					dimensionDataMap.get(dimensionList.get(0)), xDimDisplayLevel,
					xAxisDisplayLevelIds);
		}

		Map<String, String> factTableValueMap = getDataFromAlarmTable();

//		if(dimensionList.get(0).equals("DIM_SUBSTATION")) {
//			showZeroData = false;			
//		} else {
//			showZeroData = true;
//		}		
		
		groupedFactDataDisplayLevelWise = groupFactTableDataDisplayLabelWise(aggregateOperation,
				factTableValueMap, groupedXdimIdMap, dimensionList.get(0), xDimDisplayLevel,
				xAxisDisplayLevelIds, showZeroData);

		return groupedFactDataDisplayLevelWise;
	}


	private <T> Map<String, Float> groupFactTableDataDisplayLabelWise(String aggregateOperation,
			Map<String, String> factTableValueMap, Map<String, String> groupedXdimIdMap,
			String xDimName, String xAxisDisplayLevel, List<T> xAxisDisplayLevelIds,
			boolean showZeroData) {
		
		System.out.println("Inside groupFactTableDataDisplayLabelWise() method....");

		Map<String, Float> groupedFactDataDisplayLevelWise = new HashMap<String, Float>();
		Map<String, Float> avgFactDataDisplayLevelWise = new HashMap<String, Float>();
		String xAxisDisplayLevelData;
		Float aggregateValue = 0f;
		Float singleValue = 0f;

		if (factTableValueMap != null) {
			if (showZeroData) {
				for (int i = 0; i < xAxisDisplayLevelIds.size(); i++) { 
					if(xDimName.equals("DIM_CUSTOMER")) {
						System.out.println("Inside ...DIM_CUSTOMER");
						groupedFactDataDisplayLevelWise.put(groupedXdimIdMap.get(xAxisDisplayLevelIds.get(i)) + "",
								aggregateValue);
					} else {
						groupedFactDataDisplayLevelWise.put(xAxisDisplayLevelIds.get(i) + "",
						aggregateValue);
					}
					/*
					if(groupedXdimIdMap.get(xAxisDisplayLevelIds.get(i))!=null) {
						groupedFactDataDisplayLevelWise.put(groupedXdimIdMap.get(xAxisDisplayLevelIds.get(i)) + "",
								aggregateValue);
					} else {
						//System.out.println("TRANSLATION-ERROR: " +xAxisDisplayLevelIds.get(i));
					}
					*/
				}
			}
			for (Entry<String, String> factTableValue : factTableValueMap.entrySet()) {
				if (groupedXdimIdMap.containsKey(factTableValue.getKey().trim())) {
					xAxisDisplayLevelData = groupedXdimIdMap.get(factTableValue.getKey().trim());
					singleValue = Float.parseFloat(factTableValue.getValue());
					if (groupedFactDataDisplayLevelWise.containsKey(xAxisDisplayLevelData)) {
						aggregateValue = groupedFactDataDisplayLevelWise.get(xAxisDisplayLevelData);
					} else {
						aggregateValue = 0f;
					}

					if (aggregateOperation.equalsIgnoreCase("SUM")
							|| aggregateOperation.equalsIgnoreCase("AVG")) {
						aggregateValue += singleValue;
					} else if (aggregateOperation.equalsIgnoreCase("MAX")) {
						if (singleValue > aggregateValue) {
							aggregateValue = singleValue;
						}
					} else if (aggregateOperation.equalsIgnoreCase("MIN")) {
						if (singleValue < aggregateValue) {
							aggregateValue = singleValue;
						}
					}
					groupedFactDataDisplayLevelWise.put(xAxisDisplayLevelData, aggregateValue);
				} else {
					System.out.println("ERROR: Fact Table data: " + factTableValue.getValue()
							+ " : CAN'T be mapped to x-Axis Dipslay Level as mapping of: "
							+ utilCommon.getIdColumnForDimension(xDimName) + " = "
							+ factTableValue.getKey() + " To X-Axis Display Level = "
							+ xAxisDisplayLevel + " is NOT FOUND");
				}
			}
			if (aggregateOperation.equalsIgnoreCase("AVG")) {
				Iterator<String> itr = groupedFactDataDisplayLevelWise.keySet().iterator();
				String key;
				while (itr.hasNext()) {
					key = (String) itr.next();
					avgFactDataDisplayLevelWise.put(key, groupedFactDataDisplayLevelWise.get(key)
							/ xAxisDisplayLevelIds.size());
				}
				return avgFactDataDisplayLevelWise;
			} else {
				return groupedFactDataDisplayLevelWise;
			}
		} else {
			return null;
		}
	}

	private <T> Map<String, List<T>> bindXdimDisplayLevelAndIds(String xDimDisplayLevel,
			List<T> xAxisDisplayLevelIds) {
		Map<String, List<T>> bindingMap = new HashMap<String, List<T>>();
		bindingMap.put(xDimDisplayLevel, xAxisDisplayLevelIds);
		return bindingMap;
	}

	private <T> Map<String, String> groupXaxisIdsByDisplayLevel(String dimName,
			Map<String, Object> dimDataMap, String displayLevel, List<T> xAxisDisplayLevelIds) {
		System.out.println("Inside groupXaxisIdsByDisplayLevel() method....");
		String tableName = utilCommon.getTableForDimension(dimName);
		Map<String, List<T>> xdimDisplayLevelAndIdsBindingMap = bindXdimDisplayLevelAndIds(
				xDimDisplayLevel, xAxisDisplayLevelIds);
		ArrayList<String> outputColumnList = new ArrayList<String>();

		outputColumnList.add(utilCommon.getIdColumnForDimension(dimName));
		outputColumnList.add(displayLevel);

		return dimensionDAO.getResultData(tableName, outputColumnList, dimDataMap,
				xdimDisplayLevelAndIdsBindingMap, true);
	}

	private <T> List<T> getDisplayLevelIds(String dimName, Map<String, Object> dimDataMap,
			String dispLevelName) {
		System.out.println("Inside getDisplayLevelIds() method..");
		List<T> lowerLevelDimIdList = populatelDimIdsOfRequiredLevel(dimName, dimDataMap,
				dispLevelName);
		return lowerLevelDimIdList;
	}

	@SuppressWarnings("unchecked")
	private String getXdimDisplayLevel(String dimName, Map<String, Object> dimDataMap,
			boolean nextLevelGroupingRequired) {
		System.out.println("Inside getXdimDisplayLevel() method ....");
		System.out.println(("dimName....."+dimName));
		// ArrayList<String> dimList = new ArrayList<String>(dimDataMap.keySet());
		ArrayList<String> dimList = null;
		if (dimDataMap != null) {
			dimList = new ArrayList<String>(dimDataMap.keySet());
		}
		String lowestDimLevelNameFromList = utilCommon
				.getLowestDimLevelFromList(dimName, dimList).toString().trim();
		if (nextLevelGroupingRequired) {
			return utilCommon.getNextHierarchyLevelName(dimName,
					lowestDimLevelNameFromList);
		} else {
			System.out.println("lowestDimLevelNameFromList value is ...."+lowestDimLevelNameFromList);
			return lowestDimLevelNameFromList;
		}
	}

	// if dimLevelName not specified it will return Ids based on the lowestLevel
	@SuppressWarnings("unchecked")
	private <T> List<T> populatelDimIdsOfRequiredLevel(String dimName,
			Map<String, Object> dimDataMap, String dimLevelName) {
		System.out.println("Inside populatelDimIdsOfRequiredLevel() method...");
		//dimensionDAO = dimensionDAOFactory.getDimensionDAO(dimName);
		if (dimLevelName == null) {
			System.out.println("zzz-1-" + dimName + ":" + dimDataMap + ":" + dimLevelName);
			return (List<T>) dimensionDAO
					.getDynamicDistinctList(dimName, utilCommon
							.getIdColumnForDimension(dimName), String.class, dimDataMap, true);
		} else {
			System.out.println("zzz-2-" + dimName + ":" + dimDataMap + ":" + dimLevelName);
			return (List<T>) dimensionDAO.getDynamicDistinctList(dimName, dimLevelName,
					String.class, dimDataMap, true);
		}
	}

	private void pouplateDimensions(List<Map<String, Object>> dimDataMapList, List<String> dimList) {
		dimensionList = dimList;
		dimensionDataMap = new HashMap<String, Map<String, Object>>();
		for (int i = 0; i < dimList.size(); i++) {
			dimensionDataMap.put(dimList.get(i), dimDataMapList.get(i));
			System.out.println("XXX-Dimension:" + dimList.get(i) + " : BreadCrumbMap: "
					+ dimDataMapList.get(i));
		}
		System.out.println("XXX-DimensionDataMap:" + dimensionDataMap);
	}

	@SuppressWarnings("unchecked")
	private void pouplateLowestLevelIds() {
		lowestLevelIdListMap = new HashMap<String, List<String>>();
		System.out.println("DIM-DATA-MAP:" + dimensionDataMap);
		for (int i = 0; i < dimensionList.size(); i++) {
			if (dimensionDataMap.get(dimensionList.get(i)) != null) {
				System.out.println("NON-BLANK:" + dimensionList.get(i) + " :DimensionDataMap:"
						+ dimensionDataMap.get(dimensionList.get(i)));
				lowestLevelIdListMap.put(
						dimensionList.get(i),
						(List) populatelDimIdsOfRequiredLevel(dimensionList.get(i),
								dimensionDataMap.get(dimensionList.get(i)), utilCommon
										.getIdColumnForDimension(dimensionList.get(i))));
			} else {
				System.out.println("BLANK:" + dimensionList.get(i) + " :DimensionDataMap:"
						+ dimensionDataMap);
				lowestLevelIdListMap.put(dimensionList.get(i), null);
			}
		}
	}

	private Map<String, String> getDataFromFactTable() {
		System.out.println("Insert into getDataFromFactTable() method........");
		String tableName = "FACT_METER_TABLE";
		String aggregateColumn = "AVG_CONSUMPTION";
		String aggregateOperation = "SUM";

		ArrayList<String> outputColumnList = new ArrayList<String>();
		outputColumnList.add(utilCommon.getIdColumnForDimension(
				dimensionList.get(0)));

		ArrayList<String> groupByColumnList = new ArrayList<String>();
		groupByColumnList.add(utilCommon.getIdColumnForDimension(
				dimensionList.get(0)));

		Map<String, List<String>> inCriteriaMap = new HashMap<String, List<String>>();
		for (int i = 0; i < dimensionList.size(); i++) {
			inCriteriaMap.put(
					utilCommon.getIdColumnForDimension(dimensionList.get(i)),
					(List<String>) lowestLevelIdListMap.get(dimensionList.get(i)));
			// inCriteriaMap.put(dimensionList.get(i),lowestLevelIdListMap.get(dimensionList.get(i)));
			System.out.println("YYY-Dimension:"
					+ dimensionList.get(i)
					+ "ID: "
					+ utilCommon.getIdColumnForDimension(dimensionList.get(i))
					+ " : Criteria: "
					+ inCriteriaMap.get(utilCommon.getIdColumnForDimension(
							dimensionList.get(i))));
		}

		Map<String, String> factTableValueMap = dimensionDAO.getDataFromFactTable(tableName,
				aggregateOperation, aggregateColumn, outputColumnList, null, inCriteriaMap,
				groupByColumnList, true);

		return factTableValueMap;
	}
	
	private Map<String, String> getDataFromAlarmTable() 
	{
		String tableName = "FACT_ALARM_TABLE";
		String aggregateColumn = "NO_OF_ALARM";
		String aggregateOperation = "SUM";

		ArrayList<String> outputColumnList = new ArrayList<String>();
		outputColumnList.add(utilCommon.getIdColumnForDimension(
				dimensionList.get(0)));

		ArrayList<String> groupByColumnList = new ArrayList<String>();
		groupByColumnList.add(utilCommon.getIdColumnForDimension(
				dimensionList.get(0)));

		Map<String, List<String>> inCriteriaMap = new HashMap<String, List<String>>();
		for (int i = 0; i < dimensionList.size(); i++) {
			inCriteriaMap.put(
					utilCommon.getIdColumnForDimension(dimensionList.get(i)),
					(List<String>) lowestLevelIdListMap.get(dimensionList.get(i)));
			// inCriteriaMap.put(dimensionList.get(i),lowestLevelIdListMap.get(dimensionList.get(i)));
			System.out.println("YYY-Dimension:"
					+ dimensionList.get(i)
					+ "ID: "
					+ utilCommon.getIdColumnForDimension(dimensionList.get(i))
					+ " : Criteria: "
					+ inCriteriaMap.get(utilCommon.getIdColumnForDimension(
							dimensionList.get(i))));
		}

		Map<String, String> factTableValueMap = dimensionDAO.getDataFromFactTable(tableName,
				aggregateOperation, aggregateColumn, outputColumnList, null, inCriteriaMap,
				groupByColumnList, true);

		return factTableValueMap;
	}
}
